#include <pthread.h>
#include <netinet/in.h>

#include "dmacard.h"
#include "utils.h"
#include "pps.h"
#include "ppsadapter.h"
#include "config.h"
#include <time.h>
#include <syslog.h>

pthread_key_t pthkey;
struct card_thread_map CARD_THR_MAP;
//card thread map
void init_card_thread_map()
{
	CARD_THR_MAP.card_num = 0;
}

int  add_card_thread_map(pthread_t tid,char *dev)
{
	char filename[50];

    printf(" CARD_THR_MAP.card_num = %d, tid= %u, dev = %s\n", CARD_THR_MAP.card_num, tid, dev);

	if(CARD_THR_MAP.card_num >= MAX_CARD_THREAD_NUM -1)
		return -1;
    
	CARD_THR_MAP.card_thread_array[CARD_THR_MAP.card_num] = tid;
    
	sprintf(filename,"httplog_%s_%d_0",dev,CARD_THR_MAP.card_num);
	CARD_THR_MAP.log_fd0[CARD_THR_MAP.card_num] = fopen(filename,"w+");
    
	sprintf(filename,"httplog_%s_%d_1",dev,CARD_THR_MAP.card_num);
	CARD_THR_MAP.log_fd1[CARD_THR_MAP.card_num] = fopen(filename,"w+");
    
	sprintf(filename,"httplog_%s_%d_2",dev,CARD_THR_MAP.card_num);
	CARD_THR_MAP.log_fd2[CARD_THR_MAP.card_num] = fopen(filename,"w+");

	sprintf(filename,"domain_res_%s_%d",dev,CARD_THR_MAP.card_num);
	CARD_THR_MAP.log_fd3[CARD_THR_MAP.card_num] = fopen(filename,"w+");
    
	CARD_THR_MAP.card_num++;
	return 0;
}

int  get_card_thread_no(pthread_t tid)
{
	int i;
	for(i=0; i<CARD_THR_MAP.card_num; i++)
	{
		if(tid == CARD_THR_MAP.card_thread_array[i])
			return i;
	}
	return -1;
}

FILE  *get_card_thread_fd0(pthread_t tid)
{
	int i;
	for(i=0; i<CARD_THR_MAP.card_num; i++)
	{
		if(tid == CARD_THR_MAP.card_thread_array[i])
			return CARD_THR_MAP.log_fd0[i];
	}
	return NULL;
}

FILE  *get_card_thread_fd1(pthread_t tid)
{
	int i;
	for(i=0; i<CARD_THR_MAP.card_num; i++)
	{
		if(tid == CARD_THR_MAP.card_thread_array[i])
			return CARD_THR_MAP.log_fd1[i];
	}
	return NULL;
}

FILE  *get_card_thread_fd2(pthread_t tid)
{
	int i;
	for (i=0; i<CARD_THR_MAP.card_num; i++)
	{
		if (tid == CARD_THR_MAP.card_thread_array[i])
			return CARD_THR_MAP.log_fd2[i];
	}
	return NULL;
}

FILE  *get_card_thread_fd3(pthread_t tid)
{
	int i;
	for (i=0; i<CARD_THR_MAP.card_num; i++)
	{
		if (tid == CARD_THR_MAP.card_thread_array[i])
			return CARD_THR_MAP.log_fd3[i];
	}
	return NULL;
}

int  get_card_thread_num()
{
	return CARD_THR_MAP.card_num;
}
#ifndef PPS_32
extern int start_netmap_pthread(char *ifname);


int start_packet_pthread()
{
	init_card_thread_map();
	pthread_key_create(&pthkey,NULL);
    
	for (int i = 0; i < ZCOPY_MAXCARD; i++)
    {
        if (strncmp(g_ppsconfig.dma_list[i].name, "eth", 3) == 0)
        {
            start_netmap_pthread(g_ppsconfig.dma_list[i].name);
        }   
    }
	pps_dbg_print(3, PROTOCOL_GLOBE, "start packet process threads for  networkcard.");
	return 0;
}
#else	//pps_32


static	int	dev_mem_fd(-1);
static	char	*e1000_addr(NULL);
static	unsigned char *dma_buf_base[ZCOPY_MAXCARD];
struct buf_pool pkt_pool[ZCOPY_MAXCARD_10G][ZCOPY_MAXQUEUE_10G];
static	ZCOPY_DEV dev_list[ZCOPY_MAXCARD];
static int dev_fd[ZCOPY_MAXCARD];
int zparm[2];

int init_dma_devices()
{
	int i,rval = 0;
	int dev_major = 127;
	char tmpstr[20];
	rval = create_dma_dev("/dev/dma_drv", dev_major, 0);
	if (rval != 0)
		goto out;
	for( i=0;i<ZCOPY_MAXCARD;i++){
		sprintf(tmpstr,"/dev/dma_drv%d",i);
		rval = create_dma_dev(tmpstr, dev_major, i);
		if (rval != 0)
			goto out;
	}
out:
	return rval;
}

int start_packet_pthread()
{
	int thrnum=0,ret;
	pthread_t threadPacketHandler[ZCOPY_MAXCARD];
	init_card_thread_map();
	pthread_key_create(&pthkey,NULL);
	for(int i=0;i< ZCOPY_MAXCARD;i++){
		if(strncmp(dev_list[i].if_name, "eth", 3) == 0){
			pthread_create(&(threadPacketHandler[i]), NULL, ph_sendto, (void *)pps_get_pth_queue(i));
			ret=pthread_create(&(threadPacketHandler[i]), NULL, recvfrom_netdev, (void *)i);
			if(ret!=0)
				return -1;
			if(add_card_thread_map(threadPacketHandler[i]) != 0)
				return -1;
			pps_dbg_print(4,PROTOCOL_GLOBE,"start thread for %s",dev_list[i].if_name);
			thrnum++;
		}
	}
	pps_dbg_print(3,PROTOCOL_GLOBE,"start %d packet process threads.",thrnum);
	return 0;
}

int init_dmacard()
{
	int	  i=0,index;
	ZCOPY_CARD	  *card_info;

	FILE	*fp;
	char	addr[20];
	unsigned int	manager_block;

	dev_mem_fd = -1;
	e1000_addr = NULL;

	if (getuid() != 0){
		return -1;
	}
	// open mmap
	/*open device 'mem' as a media to access the RAM*/
	dev_mem_fd = open("/dev/mem",O_RDWR);
	if (dev_mem_fd < 0)
		return -2;

	fp = fopen("/proc/dma_addr", "r");
	if (fp) {
		fgets(addr, 20, fp);
		sscanf(addr, "%u", &manager_block);
		//fprintf(stderr, "Manager block: %u [%8x]\n", manager_block, manager_block);
		fclose(fp);

		/*Map the address in kernel to user space, use mmap function*/
		e1000_addr = (char *)mmap(0,4*1024, PROT_READ|PROT_WRITE, MAP_SHARED, dev_mem_fd, (unsigned long)(__pa(manager_block)));
	} 
	else {
		close(dev_mem_fd);
		return -3;
	}

	if (!e1000_addr)
		return	-1;

	card_info = (ZCOPY_CARD *)e1000_addr;
    
	for(index=0; index<ZCOPY_MAXCARD; index++){
		card_info[index].if_name[5] = '\0';
	}

	for (index = 0; index < ZCOPY_MAXCARD; index++){
		printf(" card_info[%d].if_name = %s \n", index, card_info[index].if_name);
	}
    
	for (index = 0; index < ZCOPY_MAXCARD; index++){
		if (strncmp(card_info[index].if_name, "eth", 3) == 0){
			dev_list[i].index=index;
			dev_list[i].first=1;
			dev_list[i].direct=pps_get_direct(card_info[index].if_name);
            
			if(dev_list[i].direct==-1){
				dev_list[index].if_name[0]=0;
				continue;
			}

			strcpy(dev_list[i].if_name,card_info[index].if_name);
			pps_dbg_print(4,PROTOCOL_GLOBE,"dmacard init name=%s direct=%d\n", card_info[index].if_name,dev_list[i].direct);
			i++;
		}else
		{
			dev_list[index].if_name[0]=0;
		}
	}

	if (i == 0)
		return	-2;
	for(;i>0;i--){
		dma_buf_base[dev_list[i-1].index] = (unsigned char *)mmap(0,
			card_info[dev_list[i-1].index].mem_len,
			PROT_READ|PROT_WRITE,
			MAP_SHARED,
			dev_mem_fd,
			card_info[dev_list[i-1].index].mem_start);
	}

	return  0;
}

void destroy_dmacard()
{
	int i;
	ZCOPY_CARD *card_info;

	if (e1000_addr){
		card_info = (ZCOPY_CARD *)e1000_addr;
		for(i=0;i<ZCOPY_MAXCARD;i++){
			if(dev_list[i].if_name[0]!=0)
				munmap(dma_buf_base[dev_list[i].index], card_info[dev_list[i].index].mem_len);
		}

		munmap(e1000_addr, PAGES*4*1024);
	}

	if (dev_mem_fd >= 0)
		close(dev_mem_fd); 

	e1000_addr = NULL;
	dev_mem_fd = -1;
	pps_dbg_print(4,PROTOCOL_GLOBE,"destroy_dmacard.");
}

//static unsigned int seq = 0;

int zcopy_check_e1000(int i)
{
	volatile ZCOPY_CARD	  *card_info = (ZCOPY_CARD *)e1000_addr;
	//	static int first = 1;
	char buf[10];
	if(dev_list[i].first==0){
		read(dev_fd[dev_list[i].index], buf, 10);
	}

	if (dev_list[i].first==1){
		dev_list[i].first = 0;
	}

	if(dev_list[i].if_name[0]!=0){
		card_info[dev_list[i].index].flag = false;
		dev_list[i].new_pos.curr_pkt = card_info[dev_list[i].index].curr_pkt;
	}
	return 0;
}

void* recvfrom_netdev(void *p)
{
	int i=(int)p;
	//char	*ptr;
	ZCOPY_PKTHEAD	*pkt_head;
	volatile ZCOPY_CARD	*card_info;
	unsigned int pkt_count = 0;
	//FILE *fp = NULL;
	char buf[20];
	int op,pno;
	int fquit;
	pno=i;
	pthread_setspecific(pthkey,&pno);
	//pps_dbg_print(3,PROTOCOL_GLOBE,"recvfrom_netdev netcard:%s.",dev_list[i].if_name[0]);
	card_info = (ZCOPY_CARD *)e1000_addr;

	if(dev_list[i].if_name[0]!=0){
		dev_list[i].old_pos.curr_pkt = card_info[dev_list[i].index].curr_free;
		while (1)
		{
			pkt_head = (ZCOPY_PKTHEAD *)(dma_buf_base[dev_list[i].index]+dev_list[i].old_pos.curr_pkt*PKT_BUF_SZ);
			if (pkt_head->op == 0 && dev_list[i].old_pos.curr_pkt != card_info[dev_list[i].index].curr_pkt) {
				dev_list[i].old_pos.curr_pkt = (dev_list[i].old_pos.curr_pkt+1)%card_info[dev_list[i].index].pkt_num;
				continue;
			}
			else
				break;
		}
	}

	sprintf(buf, "/dev/dma_drv%d",dev_list[i].index);
	dev_fd[dev_list[i].index] = open(buf, O_RDONLY);
	if (dev_fd[dev_list[i].index] == 0){
		printf("error open device file\n");
		pps_dbg_print(0,PROTOCOL_GLOBE,"recvfrom_netdev:error open device file\n");
		return NULL;
	}
	while (1){

		//check protocol status
		pps_check_module();
		//	grab_loop:
		if (zcopy_check_e1000(i) < 0){
			pps_dbg_print(0,PROTOCOL_GLOBE, "recvfrom_netdev(%d):zcopy_check error.\n",i);
			break;
		}
		pkt_count = 0;
		if(dev_list[i].if_name[0]!=0){
			fquit = false;
			while(1)
			{
				{
					pkt_count= card_info[dev_list[i].index].curr_pkt- dev_list[i].old_pos.curr_pkt ;
					if(pkt_count<0)
						pkt_count+=card_info[dev_list[i].index].pkt_num;
					/*		if(pkt_count<500){
					usleep(500);
					continue;
					}
					*/		
				}
				pkt_head = (ZCOPY_PKTHEAD *)(dma_buf_base[dev_list[i].index]+dev_list[i].old_pos.curr_pkt*PKT_BUF_SZ);
				op = pkt_head->op;
				switch (op)
				{
				case 0:		// free
				case 2:		// dma
					fquit = true;
					break;
				case 1:		// used
					/*process packet*/
					process_pkt(null, (u_char*)(pkt_head->data), pkt_head->len,dev_list[i].direct);
					/*process_pkt((u_char*)(pkt_head->data), pkt_head->len,dev_list[i].direct); */
					pkt_head->op = 0;
					dev_list[i].old_pos.curr_pkt = (dev_list[i].old_pos.curr_pkt+1)%(card_info[dev_list[i].index].pkt_num);
					pkt_count ++;
					break;
				case 3:		// error
					pkt_head->op = 0;
					dev_list[i].old_pos.curr_pkt = (dev_list[i].old_pos.curr_pkt+1)%(card_info[dev_list[i].index].pkt_num);
					break;
				default:
					break;
				}

				if (fquit)
					break;
			}
		}
	}

	return NULL;
}
/*for 10G card*/
int start_packet_pthread_10G()
{
	int thrnum=0,ret;
	pthread_t threadPacketHandler[ZCOPY_MAXCARD_10G];
	init_card_thread_map();
	pthread_key_create(&pthkey,NULL);
	for(int i=0;i< ZCOPY_MAXCARD_10G;i++){
		if(strncmp(dev_list[i].if_name, "eth", 3) == 0){
			for(int j=0;j< ZCOPY_MAXQUEUE_10G;j++){
				zparm[0]=i;
				zparm[1]=j;
				ret=pthread_create(&(threadPacketHandler[i]), NULL, recvfrom_netdev_10G, (void *)zparm);
				if(ret!=0)
					return -1;
				if(add_card_thread_map(threadPacketHandler[i]) != 0)
					return -1;
				usleep(10000);
				pps_dbg_print(4,PROTOCOL_GLOBE,"start thread for %s",dev_list[i].if_name);
				thrnum++;
			}
		}
	}
	pps_dbg_print(3,PROTOCOL_GLOBE,"start %d packet process threads for 10G card.",thrnum);
	return 0;
}

int init_dmacard_10G()
{
	int     i=0,j,n,index;
	ZCOPY_CARD_10G  *card_info,tmpcard;
	FILE  *fp;
	char  buf[200],addr[20];
	unsigned char *address;
	unsigned int  manager_block;
	//ZCOPY_PKTHEAD_10G *tmpbuf;
	dev_mem_fd = -1;
	e1000_addr = NULL;

	if (getuid() != 0){
		return -1;
	}
	// open mmap
	/*open device 'mem' as a media to access the RAM*/
	dev_mem_fd = open("/dev/mem",O_RDWR);
	if (dev_mem_fd < 0)
		return -2;
    
	fp = fopen("/proc/dma_addr", "r");
	if (fp) {
		fgets(addr, 20, fp);
		sscanf(addr, "%u", &manager_block);
		//fprintf(stderr, "Manager block: %u [%8x]\n", manager_block, manager_block);
		fclose(fp);

		/*Map the address in kernel to user space, use mmap function*/
		e1000_addr = (char *)mmap(0,sizeof(ZCOPY_CARD_10G)*ZCOPY_MAXCARD_10G, PROT_READ|PROT_WRITE, MAP_SHARED, dev_mem_fd, (unsigned long)(__pa(manager_block)));
	}
	else {
		close(dev_mem_fd);
		return -3;
	}

	if (!e1000_addr)
		return  -1;
	card_info = (ZCOPY_CARD_10G *)e1000_addr;
	memcpy(&tmpcard,card_info,sizeof(ZCOPY_CARD_10G));
	for(index=0; index<ZCOPY_MAXCARD_10G; index++){
		card_info[index].if_name[5] = '\0';
	}

	for (index = 0; index < ZCOPY_MAXCARD_10G; index++){
		if (strncmp(card_info[index].if_name, "eth", 3) == 0){
			dev_list[i].index=card_info[index].card_id;
			dev_list[i].direct=pps_get_direct(card_info[index].if_name);
			if(dev_list[i].direct==-1){
				dev_list[i].if_name[0]=0;
				i++;
				continue;
			}

			strcpy(dev_list[i].if_name,card_info[index].if_name);
			pps_dbg_print(4,PROTOCOL_GLOBE,"dmacard init name=%s direct=%d\n", card_info[index].if_name,dev_list[i].direct);
			sprintf(buf, "/dev/dma_drv%d",dev_list[i].index);
			dev_fd[dev_list[i].index] = open(buf, O_RDONLY);
			if (dev_fd[dev_list[i].index] == 0){
				printf("error open device file\n");
				pps_dbg_print(0,PROTOCOL_GLOBE,"recvfrom_netdev:error open device file\n");
				return -1;
			}
			i++;
		}else
		{
			dev_list[i].if_name[0]=0;
			i++;
		}
	}

	if (i == 0)
		return  -2;
	for(;i>0;i--){
		if (strncmp(card_info[i-1].if_name, "eth", 3) == 0){
			int m=0;
			for(m=0;m<ZCOPY_MAXQUEUE_10G;m++){
				for(j=0;j<ZCOPY_CARD_MEM_BLOCK_NUM;j++){
					address = (unsigned char *)mmap(0,
						PKT_MEM_BLOCK_SIZE,
						PROT_READ|PROT_WRITE,
						MAP_SHARED,
						dev_mem_fd,
						card_info[i-1].mem_start[m][j]);
					if(address==NULL){
						return -1;
					}
					for(n=0;n<PKT_MEM_BLOCK_SIZE/PKT_BUF_SZ;n++){
						pkt_pool[i-1][m].pkt_pool[j*PKT_MEM_BLOCK_SIZE/PKT_BUF_SZ+n]=
							(ZCOPY_PKTHEAD_10G *)(address+PKT_BUF_SZ*n);
					}
				}
			}
		}
	}

	return  0;
}

void destroy_dmacard_10G()
{
	int i;
	ZCOPY_CARD_10G *card_info;

	if (e1000_addr){
		card_info = (ZCOPY_CARD_10G *)e1000_addr;
		for(i=0;i<ZCOPY_MAXCARD_10G;i++){
			if(dev_list[i].if_name[0]!=0)
				munmap(dma_buf_base[i], PKT_MEM_BLOCK_SIZE);
		}

		munmap(e1000_addr, 128*4*1024);
	}

	if (dev_mem_fd >= 0)
		close(dev_mem_fd);

	e1000_addr = NULL;
	dev_mem_fd = -1;
	pps_dbg_print(4,PROTOCOL_GLOBE,"destroy_dmacard.");
}

int zcopy_check_10G(int i,int j)
{
	char buf[20];
	read(dev_fd[dev_list[i].index], buf, j);
	return 0;
}

void* recvfrom_netdev_10G(void *p)
{
	int *parm=(int *)p;
	int i=parm[0],j=parm[1];
	//char    *ptr;
	ZCOPY_PKTHEAD_10G *pkt_head;
	volatile ZCOPY_CARD_10G   *card_info;
	unsigned int pkt_count = 0;
	//FILE *fp = NULL;
	int op;
	int fquit;
	int pno=i*ZCOPY_MAXQUEUE_10G+j;
	pthread_setspecific(pthkey,&pno);
	volatile struct packet_queue *pqueue;
	//  pps_dbg_print(3,PROTOCOL_GLOBE,"recvfrom_netdev netcard:%s.",dev_list[i].if_name);
	card_info = (ZCOPY_CARD_10G *)e1000_addr;
	pqueue=&(card_info[i].cache_queue[j]);
	//check protocol status
	while(1){
		pps_check_module();
		//  grab_loop:
		//printf("i=%d,queue:%d,num:%d head=%d tail=%d,id=%d\n",i,j,pqueue->num,pqueue->head,pqueue->tail,pqueue->queue[pqueue->head]);
		if (zcopy_check_10G(i,j) < 0){
			pps_dbg_print(0,PROTOCOL_GLOBE, "recvfrom_netdev(%d):zcopy_check error.\n",i);
			break;
		}
		pkt_count = 0;
		if(dev_list[i].if_name[0]!=0){
			fquit = false;
			while(1)
			{
				//if(j==2)
				//syslog(LOG_USER|LOG_WARNING,"i=%d,queue:%d,num:%d head=%d tail=%d,id=%d\n",i,j,pqueue->num,pqueue->head,pqueue->tail,pqueue->queue[pqueue->head]);
				if(pqueue->head==pqueue->tail){
					//syslog(LOG_USER|LOG_WARNING,"wait,i=%d,queue:%d,num:%d head=%d tail=%d,tail_id=%d\n",i,j,pqueue->num,pqueue->head,pqueue->tail,pqueue->queue[pqueue->tail]);
					//  continue;
					break;
				}

				if(pqueue->queue[pqueue->head]==-1){
					syslog(LOG_USER|LOG_WARNING,"error1,i=%d,queue:%d,num:%d head=%d tail=%d,id=%d\n",i,j,pqueue->num,pqueue->head,pqueue->tail,pqueue->queue[pqueue->head]);
					//        pqueue->head = (pqueue->head+1)%PKT_QUEUE_SZ_10G;
					//  continue;
					break;
				}
				pkt_head = (ZCOPY_PKTHEAD_10G *)(pkt_pool[i][j].pkt_pool[pqueue->queue[pqueue->head]]);
				if(pkt_head->op == BLOCK_10G_QUEUE){
					syslog(LOG_USER|LOG_WARNING,"error2,i=%d,queue:%d,num:%d head=%d tail=%d,id=%d\n",i,j,pqueue->num,pqueue->head,pqueue->tail,pqueue->queue[pqueue->head]);
					//      pqueue->head = (pqueue->head+1)%PKT_QUEUE_SZ_10G;
					//	continue;
					break;
				}
				op = pkt_head->op;
				if(op == BLOCK_10G_USED){
					process_pkt(null, (u_char*)(pkt_head->data), pkt_head->len,dev_list[i].direct);
                                        /*process_pkt((u_char*)(pkt_head->data), pkt_head->len,dev_list[i].direct); */
					pkt_head->op = BLOCK_10G_QUEUE;
					//      pqueue->queue[pqueue->head]=-1;
					pqueue->head = (pqueue->head+1)%PKT_QUEUE_SZ_10G;
					//      pqueue->num--;
					pkt_count ++;
				}
			}
		}
	}

	return NULL;
}

#endif
